﻿import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)

# 数据
X = 2*np.random.rand(100, 1)
y = 4+3*X+np.random.randn(100, 1)
# 增加一列，用于求解偏置项b
X_b = np.c_[(np.ones((100, 1)), X)]


def learning_schedule(t):
    return 5/(50+t)


# 批量梯度下降
theta_path_bgd = []
eta = 0.1 # 学习率
n_iterations = 1000 # 迭代次数
m = len(X_b) # 样本数
np.random.seed(0)
theta = np.random.randn(2, 1) # 随机初始化参数
for iteration in range(n_iterations):
    gradients = 2/m*X_b.T.dot(X_b.dot(theta) - y)
    theta = theta - eta*gradients
    theta_path_bgd.append(theta)


# 随机梯度下降
theta_path_sgd = []
m = len(X_b)
n_epochs = 50
np.random.seed(0)
theta = np.random.randn(2, 1) # 随机初始化参数
for epoch in range(n_epochs):
    for i in range(m):
        random_index = np.random.randint(m)
        xi = X_b[random_index:random_index+1]
        yi = y[random_index:random_index+1]
        gradients = 2*xi.T.dot(xi.dot(theta) - yi)
        eta = learning_schedule(epoch*m+i)
        theta = theta-eta*gradients
        theta_path_sgd.append(theta)


# MiniBatch梯度下降
theta_path_mgd = []
m = len(X_b)
n_epochs = 50
minibatch = 16
np.random.seed(0)
theta = np.random.randn(2, 1)
t = 0
for epoch in range(n_epochs):
    shuffled_indices = np.random.permutation(m)
    X_b_shuffled = X_b[shuffled_indices]
    y_shuffled = y[shuffled_indices]
    for i in range(0, m, minibatch):
        t += 1
        xi = X_b_shuffled[i:i+minibatch]
        yi = y_shuffled[i:i+minibatch]
        gradients = 2/minibatch*xi.T.dot(xi.dot(theta) - yi)
        eta = learning_schedule(t)
        theta = theta-eta*gradients
        theta_path_mgd.append(theta)


theta_path_bgd = np.array(theta_path_bgd) # 批量梯度下降
theta_path_sgd = np.array(theta_path_sgd) # 随机梯度下降
theta_path_mgd = np.array(theta_path_mgd) # MiniBatch梯度下降

plt.figure(figsize=(12, 6))
plt.plot(theta_path_bgd[:, 0], theta_path_bgd[:, 1], 'b-o', linewidth=3, label='BGD')
plt.plot(theta_path_sgd[:, 0], theta_path_sgd[:, 1], 'r-s', linewidth=1, label='SGD')
plt.plot(theta_path_mgd[:, 0], theta_path_mgd[:, 1], 'g-+', linewidth=2, label='MINIGD')
plt.legend(loc='upper left')
plt.axis([3.5, 4.5, 2.0, 4.0])
plt.show()